package com.harboursoftware.xstorage.model;

import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;

import com.harboursoftware.xstorage.ac.AccessControlPolicy;

/**
 * A bucket is owned by the AWS account (identified by AWS Access Key ID) that created it. Each AWS account can own up
 * to 100 buckets at a time. Bucket ownership is not transferable. However, if a bucket is empty, it can be deleted and
 * its name can be reused.
 * 
 * @author Simon Leung
 * @since 0.1
 * @XService
 * @XObject
 */
public class XBucket extends XElement {
    private String name;
    private XUser owner;
    private AccessControlPolicy acp;
    private Date creationDate;
    private List<XObject> objects;
    // numbers of objects in this bucket
    private int size;

    public XBucket() {
    }

    public XBucket(String name) {
        this.name = name;
    }

    public XBucket(String name, XUser owner, AccessControlPolicy acp) {
        this.name = name;
        this.owner = owner;
        this.acp = acp;
    }

    /**
     * refer to <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/dev/index.html?UsingBucket.html">Amazon
     * S3 Bucket Restrictions and Limitations</a>. Returns true if the bucket name can be used as a component of a valid
     * DNS name. If so, the bucket can be accessed using requests with the bucket name as part of an S3 sub-domain. If
     * not, the old-style bucket reference URLs must be used, in which case the bucket name must be the first component
     * of the resource path.
     * 
     * @return true if the name of bucket is valid
     * @see org.jets3t.service.S3Service#isBucketNameValidDNSName(String bucketName)
     */
    public boolean hasValidDNSName() {
        if (name == null || name.length() > 63 || name.length() < 3) {
            return false;
        }

        // Only lower-case letters, numbers, '.' or '-' characters allowed
        if (!Pattern.matches("^[a-z0-9][a-z0-9.-]+$", name)) {
            return false;
        }

        // Cannot be an IP address, i.e. must not contain four '.'-delimited
        // sections with 1 to 3 digits each.
        if (Pattern.matches("([0-9]{1,3}\\.){3}[0-9]{1,3}", name)) {
            return false;
        }

        // Components of name between '.' characters cannot start or end with '-',
        // and cannot be empty
        String[] fragments = name.split("\\.");
        for (int i = 0; i < fragments.length; i++) {
            if (Pattern.matches("^-.*", fragments[i]) || Pattern.matches(".*-$", fragments[i])
                || Pattern.matches("^$", fragments[i])) {
                return false;
            }
        }
        return true;
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }

    /**
     * get the name of bucket.
     * 
     * @return
     */
    public String getName() {
        return name;
    }

    /**
     * set the name of bucket.
     * 
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    public AccessControlPolicy getAcp() {
        return acp;
    }

    public void setAcp(AccessControlPolicy acp) {
        this.acp = acp;
    }

    public XUser getOwner() {
        return owner;
    }

    public void setOwner(XUser owner) {
        this.owner = owner;
    }

    /**
     * get objects in this bucket.
     * 
     * @return
     */
    @SuppressWarnings("unchecked")
    public List<XObject> getObjects() {
        return objects == null ? Collections.EMPTY_LIST : objects;
    }

    /**
     * set objects in this bucket.
     * 
     * @param objects
     */
    public void setObjects(List<XObject> objects) {
        this.objects = objects;
    }

    public Date getCreationDate() {
        return creationDate;
    }

    public void setCreationDate(Date creationDate) {
        this.creationDate = creationDate;
    }

    /**
     * get the numbers of objects in this bucket.
     * 
     * @return
     */
    public int getSize() {
        return size;
    }

    /**
     * set the numbers of objects in this bucket.
     * 
     * @param size
     */
    public void setSize(int size) {
        this.size = size;
    }

    @Override
    public boolean equals(Object obj) {
        return EqualsBuilder.reflectionEquals(this, obj);
    }

    @Override
    public int hashCode() {
        return HashCodeBuilder.reflectionHashCode(this);
    }
}
